package gui;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.swing.JComponent;

@SuppressWarnings("serial")
// TODO - abstract
// TODO убрать персон, создать лист изображений ?
public class Person extends JComponent {

	private Random r = new Random();

	private double x1, y1;

	private int time;
	private boolean near = false; // TODO
	private double speed = 1;

	private double dx, dy, rotInRad;

	// for goAfterPeople method
	private int health = 100;
	private int amountOfZombiesGoesAfterPeopleNow = 0;
	private boolean goesAfterPeople, transformation = false;
	private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

	public Person() {

		x1 = 25 + r.nextInt(Main.WIDTH_OF_FIELD - 40);
		y1 = 25 + r.nextInt(Main.HEIGHT_OF_FIELD - 40);
		rotInRad = Math.toRadians((365 - r.nextDouble() * 365 * 2));

	}

	protected void move() {
		time++;

		moveWithScreenRestrictions();
		if (!transformation) {
			x1 += dx;
			y1 += dy;
		}

	}

	protected void moveRandomWithRotation() {
		if (time % (r.nextInt(50) + 20) == 0) {
			doOneRandomStep();
			if (time % (r.nextInt(100) + 20) == 0) {
				// every max. 2 sec. rest for min. 1/4 sec
				dx = dy = rotInRad = 0;
			}
		}

	}

	private void doOneRandomStep() {
		rotInRad = Math.toRadians((365 - r.nextDouble() * 365 * 2));

		dx = speed * Math.cos(rotInRad);
		dy = speed * -Math.sin(rotInRad);

	}

	private void moveWithScreenRestrictions() {
		if (x1 < 20 || x1 > (Main.WIDTH_OF_FIELD - 15)) {
			dx = -dx;
		}

		if (y1 < 20 || y1 > (Main.HEIGHT_OF_FIELD - 15)) {
			dy = -dy;

		}
	}

	protected double howNear(double xto, double yto, double xfrom, double yfrom) {
		double xnr = xto - xfrom;
		double ynr = yto - yfrom;
		return Math.hypot(xnr, ynr);
	}

	/**
	 * When zombie near people (<70), he will go after them
	 * 
	 */
	protected void goAfterPeople(Person zombie) {
		if (ListOfPerson.getPeoplesList() != null) {
			for (int i = 0; i < ListOfPerson.getPeoplesList().size(); i++) {
				Person people = ListOfPerson.getPeoplesList().get(i);

				if (howNear(people.getX1(), people.getY1(), zombie.getX1(), zombie.getY1()) < 70
						&& !people.transformation) {
					zombie.goAfter(people);
					if (!zombie.goesAfterPeople) {
						people.addAmountOfZombiesGoesAfterPeopleNow();
					}
					zombie.goesAfterPeople = true;
					/*
					 * Баг когда зомби идет за человеком и его удаляешь
					 * слайдером // TODO
					 * amountOfZombiesGoesAfterPeopleNowCounted остается true
					 */

					if (howNear(people.getX1(), people.getY1(), zombie.getX1(), zombie.getY1()) < 20) {
						// смена отметки слайдера , by "remove" still the same
						// TODO
						if (people.getAmountOfZombiesGoesAfterPeopleNow() > 1) {
							zombie.goesAfterPeople = false;
							ListOfPerson.getPeoplesList().remove(people);
						} else {
							System.out.println("Man killed zombie, but would have been infected");
							people.transformation = true;
							ListOfPerson.getLiftOfZombies().remove(zombie);
							infected_WillBeZombie(people);
						}
					}
				} else if (howNear(people.getX1(), people.getY1(), zombie.getX1(), zombie.getY1()) > 70
						|| people.transformation) {
					moveRandomWithRotation();

				}
			}
		}
	}

	private void infected_WillBeZombie(Person people) {

		final Runnable beeper = new Runnable() {
			public void run() {
				System.out.println("New zombie is here!");
				Person p = new Person();
				p.setX1(people.getX1());
				p.setY1(people.getY1());
				people.transformation = false;

				ListOfPerson.getPeoplesList().remove(people);
				ListOfPerson.getLiftOfZombies().add(p);
			}
		};
		final ScheduledFuture<?> beeperHandle = scheduler.schedule(beeper, 5, TimeUnit.SECONDS);
		scheduler.schedule(new Runnable() {
			public void run() {
				beeperHandle.cancel(true);
			}
		}, 5, TimeUnit.SECONDS);
	}

	protected int reduceHealth() {
		health--;
		return health;
	}

	/**
	 * this will go after afterhim
	 * 
	 * @param afterhim
	 */
	private void goAfter(Person p) {
		double dXto = p.x1 - this.x1;
		double dYto = this.y1 - p.y1;
		this.setRotInRad(Math.atan2(dYto, dXto));
		this.dx = speed * Math.cos(rotInRad);
		this.dy = speed * -Math.sin(rotInRad);
	}

	protected void actionAtCollision() {
		if (near) {
			dx = -dx;
			dy = -dy;
			near = false;
		}
	}

	// Getter & Setter

	public double getX1() {
		return x1;
	}

	public double getY1() {
		return y1;
	}

	public void setX1(double x1) {
		this.x1 = x1;
	}

	public void setY1(double y1) {
		this.y1 = y1;
	}

	public double getDx() {
		return dx;
	}

	public void setDx(double dx) {
		this.dx = dx;
	}

	public double getDy() {
		return dy;
	}

	public void setDy(double dy) {
		this.dy = dy;
	}

	public double getRotInRad() {
		return rotInRad;
	}

	public void setRotInRad(double rotInRad) {
		this.rotInRad = rotInRad;
	}

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) {
		this.health = health;
	}

	public int getAmountOfZombiesGoesAfterPeopleNow() {
		return amountOfZombiesGoesAfterPeopleNow;
	}

	public int addAmountOfZombiesGoesAfterPeopleNow() {
		amountOfZombiesGoesAfterPeopleNow++;
		return amountOfZombiesGoesAfterPeopleNow;
	}

	public double getSpeed() {
		return speed;
	}

	public void setSpeed(double speed) {
		this.speed = speed;
	}

}
